package com.cyf.common.core.math;

import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;

/**
 * @author chenyifan
 * @create 2024-08-21 14:40
 */
public class NumberUtil {

    /**
     * 默认除法运算精度
     */
    private static final int DEFAULT_DIV_SCALE = 10;

    /**
     * 默认取舍模式：四舍五入
     */
    private static final RoundingMode DEFAULT_MODE = RoundingMode.HALF_UP;


    // ------------------------------------------------ 加法运算 -----------------------------------------

    /**
     * 加法运算
     * @param values 多个被加值
     * @return 和
     */
    public static BigDecimal add(Number... values) {
        return add(Arrays.asList(values));
    }

    /**
     * 加法运算
     * @param values 多个被加值
     * @return 和
     */
    public static BigDecimal add(List<? extends Number> values) {
        if (CollectionUtils.isEmpty(values)) {
            return BigDecimal.ZERO;
        }
        BigDecimal result = toBigDecimal(values.get(0));
        for (int i = 1; i < values.size(); i++) {
            result = result.add(toBigDecimal(values.get(i)));
        }
        return result;
    }

    // ----------------------------------------------- 减法运算 ---------------------------------------

    /**
     * 减法运算
     * @param values 多个被减值
     * @return 差
     */
    public static BigDecimal sub(Number... values) {
        return sub(Arrays.asList(values));
    }

    /**
     * 减法运算
     * @param values 多个被减值
     * @return
     */
    public static BigDecimal sub(List<? extends Number> values) {
        if (CollectionUtils.isEmpty(values)) {
            return BigDecimal.ZERO;
        }
        BigDecimal result = toBigDecimal(values.get(0));
        for (int i = 1; i < values.size(); i++) {
            result = result.subtract(toBigDecimal(values.get(i)));
        }
        return result;
    }


    // ----------------------------------------------- 乘法运算 -----------------------------------------

    /**
     * 乘法运算
     * @param values 多个被乘值
     * @return 积
     */
    public static BigDecimal mul(Number... values) {
        return mul(Arrays.asList(values));
    }

    /**
     * 乘法运算
     * @param values 多个被乘值
     * @return 积
     */
    public static BigDecimal mul(List<? extends Number> values) {
        if (CollectionUtils.isEmpty(values)) {
            return BigDecimal.ZERO;
        }

        BigDecimal result = toBigDecimal(values.get(0));
        for (int i = 1; i < values.size(); i++) {
            result = result.multiply(toBigDecimal(values.get(i)));
        }
        return result;
    }


    // ------------------------------------------------ 除法运算 -------------------------------------------

    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,使用默认的精度
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static BigDecimal div(Number v1, Number v2) {
        return div(v1, v2, DEFAULT_DIV_SCALE);
    }

    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,由scale指定精确度,后面的四舍五入
     *
     * @param v1 被除数
     * @param v2 除数
     * @param scale 精确度
     * @return 两个参数的商
     */
    public static BigDecimal div(Number v1, Number v2, int scale) {
        return div(v1, v2, scale, DEFAULT_MODE);
    }

    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,由scale指定精确度
     *
     * @param v1 被除数
     * @param v2 除数
     * @param scale 精确度
     * @param roundingMode 保留小数的模式
     * @return 两个参数的商
     */
    public static BigDecimal div(Number v1, Number v2, int scale, RoundingMode roundingMode) {
        if (v1 == null || v2 == null || toBigDecimal(v2).compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return toBigDecimal(v1).divide(toBigDecimal(v2), scale, roundingMode);
    }

    /**
     * 除法运算并向上取整
     * 12,5  -> 3
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static int divOfCeiling(Number v1, Number v2) {
        return div(v1, v2, 0, RoundingMode.CEILING).intValue();
    }


    // ------------------------------ 设置精度 ---------------------------------

    public static BigDecimal setScale(Number val, int scale) {
        return setScale(val, scale, DEFAULT_MODE);
    }

    public static BigDecimal setScale(Number val, int scale, RoundingMode mode) {
        return toBigDecimal(val).setScale(scale, mode);
    }


    // ---------------------------------------- 幂运算 ------------------------------------------------
    /**
     * 提供精确的幂运算
     *
     * @param number 底数
     * @param n      指数
     * @return 幂的积
     */
    public static BigDecimal pow(Number number, int n) {
        return toBigDecimal(number).pow(n);
    }


    // ---------------------------------- 数字转字符串 ---------------------------------------------------

    /**
     * 数字转字符串，并去除尾小数点儿后多余的0
     * @param number
     * @return
     */
    public static String toStr(Number number) {
        return toStr(number, true);
    }

    /**
     * 数字转字符串，可选去除尾小数点儿后多余的0
     * @param number
     * @param isStripTrailingZeros 是否去除末尾多余0，例如5.0返回5
     * @return
     */
    public static String toStr(Number number, boolean isStripTrailingZeros) {
        BigDecimal decimal = toBigDecimal(number);
        if (isStripTrailingZeros) {
            decimal = decimal.stripTrailingZeros();
        }
        return decimal.toPlainString();
    }

    /**
     * 数字转 BigDecimal
     * Float、Double等有精度问题，转换为字符串后再转换
     * null转换为0
     * @param number 数字
     * @return
     */
    public static BigDecimal toBigDecimal(Number number) {
        if (number == null) {
            return null;
        }

        if (number instanceof BigDecimal) {
            return (BigDecimal) number;
        } else if (number instanceof Long) {
            return new BigDecimal((Long) number);
        } else if (number instanceof Integer) {
            return new BigDecimal((Integer) number);
        } else if (number instanceof BigInteger) {
            return new BigDecimal((BigInteger) number);
        }

        // Float、Double等有精度问题，转换为字符串后再转换
        return new BigDecimal(number.toString());
    }
}